package com.fxu.jushuitan.util;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import com.fxu.framework.core.holder.RedisHolder;
import com.fxu.framework.core.holder.SpringHolder;
import com.fxu.jushuitan.dto.JstConfig;
import com.fxu.jushuitan.dto.JstToken;
import com.fxu.jushuitan.dto.RootRes;
import com.jushuitan.api.ApiClient;
import com.jushuitan.api.ApiRequest;
import com.jushuitan.api.ApiResponse;
import com.jushuitan.api.DefaultApiClient;
import com.jushuitan.api.exception.ApiException;
import com.jushuitan.api.util.ApiUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 聚水潭访问钩子
 * @author fangxu
 * @version 2023-08-21
 */
@Slf4j
@Component("JstHolder")
public class JstHolder {
    @Value("${jushuitan.appKey:}")
    private String appKey;
    @Value("${jushuitan.appSecret:}")
    private String appSecret;

    private static JstHolder holder = null;
    public static JstHolder getHolder() {
        return holder == null ? (holder = SpringHolder.getBean("JstHolder")) : holder;
    }

    public static JstConfig getConfig(String appKey, String appSecret, boolean log) {
        JstToken jstToken = accessToken(appKey, appSecret);
        JstConfig jstConfig = new JstConfig();
        jstConfig.setLog(log);
        jstConfig.setAppKey(appKey);
        jstConfig.setSecret(appSecret);
        jstConfig.setAccessToken(jstToken.getAccessToken());
        return jstConfig;
    }

    public String getAccessToken() {
        JstToken jstToken = accessToken();
        return jstToken.getAccessToken();
    }

    public JstToken getToken() {
        ValueOperations<String, Object> ops = RedisHolder.getRedisTemplate().opsForValue();
        Object refreshToken = ops.get("JST_REFRESH_TOKEN");
        if (refreshToken == null) {
            return RedisHolder.getIfNullSet("JST_ACCESS_TOKEN", 24 * 60 * 60, () -> {
                JstToken token = accessToken();
                ops.set("JST_REFRESH_TOKEN", token.getRefreshToken(), token.getExpiresIn(), TimeUnit.SECONDS);
                return token;
            });
        } else {
            return RedisHolder.getIfNullSet("JST_ACCESS_TOKEN", 24 * 60 * 60, () -> {
                JstToken token = refreshToken(refreshToken.toString());
                return token == null ? accessToken() : token;
            });
        }
    }

    public String query(String accessToken, String url, String biz) {
        return query(accessToken, url, biz, appKey, appSecret);
    }

    public static String query(String accessToken, String url, String biz, String appKey, String appSecret) {
        ApiClient client = new DefaultApiClient();
        // 设置超时时间
//        ((DefaultApiClient)client).setReadTimeout(3000);
//        ((DefaultApiClient)client).setConnectTimeout(2000);
        ApiRequest request = new ApiRequest.Builder(url, appKey, appSecret).biz(biz).build();
        try {
            ApiResponse response = client.execute(request, accessToken);
            if (response.isSuccess()) {
                return response.getBody();
            } else {
                log.error("接口返回错误!body={}", response.getBody());
                return null;
            }
        } catch (ApiException e) {
            log.error("请求接口错误!", e);
            error(e);
        } catch (Exception e) {
            log.error("请求接口其他错误!", e);
        }
        return null;
    }

    // 更新令牌接口地址：https://openapi.jushuitan.com/openWeb/auth/refreshToken
    // refresh_token
    // 登录的token
    // {"code":0,"msg":null,"data":{"access_token":"e8ddc7e294564b91854b832ad1d9af8d","expires_in":2344686,"refresh_token":"b4c685bbc736430ea1f1aedaafa7c639","scope":"all"}}
    // {"code":0,"data":{"access_token":"e8ddc7e294564b91854b832ad1d9af8d","refresh_token":"b4c685bbc736430ea1f1aedaafa7c639","scope":"all","expires_in":2344686}}
    public JstToken accessToken() {
        return accessToken(appKey, appSecret);
    }

    public static JstToken accessToken(String appKey, String appSecret) {
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("grant_type", "authorization_code");
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000L));
        params.put("code", RandomUtil.randomString(6));
        params.put("charset", "utf-8");
        String sign = ApiUtil.getSign(appSecret, params);
        params.put("sign", sign);

        String url = "https://openapi.jushuitan.com/openWeb/auth/getInitToken";
        String query = null;
        try {
            query = JstHelper.buildQuery(params);
        } catch (IOException e) {
            log.error("获取刷新密钥错误!", e);
        }
        String result = HttpUtil.post(url, query);
        return RootRes.toBean(result, JstToken.class);
    }

    public JstToken refreshToken(String refreshToken) {
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000L));
        params.put("grant_type", "refresh_token");
        params.put("charset", "utf-8");
        params.put("refresh_token", refreshToken);
        params.put("scope", "all");
        String sign = ApiUtil.getSign(appSecret, params);
        params.put("sign", sign);

        String url = "https://openapi.jushuitan.com/openWeb/auth/refreshToken";
        String query = null;
        try {
            query = JstHelper.buildQuery(params);
        } catch (IOException e) {
            log.error("获取刷新密钥错误!", e);
        }
        String result = HttpUtil.post(url, query);
        return RootRes.toBean(result, JstToken.class);
    }

    public static void error(ApiException e) {
        switch (e.getCodeEnum()) {
            case SYS_ERROR:
                // TODO retry
                break;
            case PARAM_ERROR:
                // TODO check param
                break;
            case SIGN_ERROR:
                // TODO @see com.jushuitan.api.util.ApiUtil
                break;
            case CONNECT_ERROR:
                // TODO check network
                break;
            case TIME_OUT_ERROR:
                // TODO check logic or reset readTimeout
                break;
            case EXECUTE_ERROR:
                // TODO @see com.jushuitan.api.DefaultApiClient#executeRemote
                break;
            default:
                // log
                break;
        }
    }
}
